package cn.lg.soar.mvc.util;

import cn.lg.soar.common.data.msg.I18n;
import cn.lg.soar.common.exceptions.AuthenticationException;
import cn.lg.soar.common.exceptions.ParameterException;
import cn.lg.soar.common.exceptions.PermissionException;
import cn.lg.soar.common.model.HttpResult;
import cn.lg.soar.common.util.FileTypeUtils;
import cn.lg.soar.common.util.data.DataUtil;
import cn.lg.soar.common.util.jackson.JacksonUtil;
import com.alibaba.excel.support.ExcelTypeEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;

/**
 * 响应工具
 * @author luguoxiang
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
public class ResponseUtils {

    protected static final Logger LOGGER = LoggerFactory.getLogger(ResponseUtils.class);

    private final static String CONTENT_TYPE_HTML = "text/html;charset=UTF-8";
    private final static String LOGIN_JSON = JacksonUtil.toJson(HttpResult.fail(new AuthenticationException()));
    private final static String PERMISSION_JSON = JacksonUtil.toJson(HttpResult.fail(new PermissionException()));
    private final static String ERROR_JSON = JacksonUtil.toJson(HttpResult.fail());

    public static void responseBody(HttpServletResponse response, String text) {
        response.setContentType(ResponseUtils.CONTENT_TYPE_HTML);
        try (PrintWriter out = response.getWriter()){
            out.write(text);
            out.flush();
        } catch (IOException e) {
            LOGGER.error("写响应内容失败", e);
        }
    }

    public static void responseBody(HttpServletResponse response, Object data) {
        ResponseUtils.responseBody(response, JacksonUtil.toJson(data));
    }

    public static void responseBody(String text) {
        responseBody(((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getResponse(), text);
    }

    public static void responseBody(Object data) {
        responseBody(((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getResponse(), JacksonUtil.toJson(data));
    }

    public static void E400(HttpServletResponse response, I18n msg, Object... args){
        response.setStatus(400);
        ResponseUtils.responseBody(response, HttpResult.fail(new ParameterException(msg, args)));
    }

    public static void E401(HttpServletResponse response){
        response.setStatus(401);
        ResponseUtils.responseBody(response, ResponseUtils.LOGIN_JSON);
    }

    public static void E403(HttpServletResponse response){
        response.setStatus(403);
        ResponseUtils.responseBody(response, ResponseUtils.PERMISSION_JSON);
    }

    public static void E412(HttpServletResponse response, String msg){
        response.setStatus(412);
        ResponseUtils.responseBody(response, msg);
    }

    public static void E500(HttpServletResponse response){
        response.setStatus(500);
        ResponseUtils.responseBody(response, ResponseUtils.ERROR_JSON);
    }

    public static void setResponseExcel(String fileName, HttpServletResponse response) {
        try {
            response.setCharacterEncoding("utf-8");
            response.setContentType("application/vnd.ms-excel");
            response.setHeader(
                    "Content-disposition",
                    "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ExcelTypeEnum.XLSX.getValue()
            );
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    /**.
     *
     *
     * @param fileName
     * @param response
     */
    public static void setResponseExcel(int status, HttpServletResponse response) {
        response.setStatus(status);
//        response.setCharacterEncoding("utf-8");
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
    }

    /**
     * 跨域配置
     * @param response
     * @param origin
     * @param methods
     */
    public static void corsConfig(HttpServletResponse response, String origin, String methods, String headers, String age) {
        response.addHeader("Access-Control-Allow-Origin", origin);
        response.addHeader("Access-Control-Allow-Methods", methods);
        response.addHeader("Access-Control-Allow-Headers", headers);
        response.addHeader("Access-Control-Allow-Max-Age", age);
    }
    public static void corsConfig(HttpServletResponse response, String origin, String methods, String headers){
        corsConfig(response, origin, methods, headers, "3600");
    }
    public static void corsConfig(HttpServletResponse response, String origin, String methods){
        corsConfig(response, origin, methods, "*", "3600");
    }
    public static void corsConfig(HttpServletResponse response, String origin){
        corsConfig(response, origin, "*", "*", "3600");
    }
    public static void corsConfig(HttpServletResponse response){
        corsConfig(response, "*", "*", "*", "3600");
    }

    public static HttpServletResponse getResponse() {
        return ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getResponse();
    }

    /**
     * 文件流响应
     * @param file 文件
     * @param filename 文件名称
     */
    public static void responseFile(File file, String filename) {
        if(!file.exists()) {
            return;
        }
        try {
            responseFile(file.length(), Files.newInputStream(file.toPath()), filename);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 文件流响应
     * @param contentLength 内容长度
     * @param inputStream 输入流
     */
    public static void responseFile(long contentLength, InputStream inputStream) {
        responseFile(contentLength, inputStream, null);
    }

    /**
     * 文件流响应
     * @param contentLength 内容长度
     * @param inputStream 输入流
     * @param filename 文件名称
     */
    public static void responseFile(long contentLength, InputStream inputStream, String filename) {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        HttpServletResponse response = getResponse();
        try (OutputStream outputStream = response.getOutputStream()){
            response.setCharacterEncoding("UTF-8");
            if (DataUtil.isValuable(filename)) {
                response.setContentType(FileTypeUtils.getContentType(filename) + "; charset=UTF-8");
                // 根据不同的客户端进行不同的编码
                String agent = request.getHeader("User-Agent");
                if (agent.contains("Firefox")) {
                    filename = "=?utf-8?B?" + DataUtil.byteToBase64(filename.getBytes(StandardCharsets.UTF_8)) + "?=";
                } else {
                    filename = URLEncoder.encode(filename, "utf-8");
                }
                response.setHeader("Content-Disposition", "attachment;fileName=\"" + filename + "\"");
            }
            // 解析Range头
            String rangeHeader = request.getHeader("Range");
            if (contentLength > 0 && StringUtils.hasText(rangeHeader)) {
                if (rangeHeader.startsWith("bytes=")) {
                    // 范围响应
                    long fileLength = contentLength;
                    long start;
                    long end = fileLength - 1;
                    // 解析范围
                    String[] ranges = rangeHeader.substring(6).split("-");
                    try {
                        start = Long.parseLong(ranges[0]);
                        if (ranges.length > 1) {
                            end = Long.parseLong(ranges[1]);
                        }
                    } catch (NumberFormatException e) {
                        throw new RuntimeException(e);
                    }
                    // 计算内容长度
                    contentLength = end - start + 1;
                    // 设置响应头
                    response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
                    response.setContentLengthLong(contentLength);
                    response.setHeader("Content-Range", "bytes " + start + "-" + end + "/" + fileLength);
                    response.setHeader("Accept-Ranges", "bytes");
                    // 跳过起始位置之前的字节
                    inputStream.skip(start);
                    byte[] buffer = new byte[4096];
                    int read;
                    while (contentLength > 0 && (read = inputStream.read(buffer, 0, (int) Math.min(buffer.length, contentLength))) != -1) {
                        outputStream.write(buffer, 0, read);
                        contentLength -= read;
                    }
                    outputStream.flush();
                    return;
                }
            }
            // 正常输出
            response.setStatus(HttpServletResponse.SC_OK);
            if (contentLength > 0) {
                response.setContentLengthLong(contentLength);
            }
            byte[] buffer = new byte[4096];
            int read;
            while ((read = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, read);
            }
            outputStream.flush();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            // 关闭输入流，输出流语法糖处理了
            try {
                inputStream.close();
            } catch (IOException e) {
                LOGGER.error("关闭流异常", e);
            }
        }
    }

}
